package com.song.client;

import com.song.transport.CommonDecoder;
import com.song.transport.CommonEncoder;
import com.song.transport.CommonSerializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;


/**
 * Netty 用于获取Channel对象
 */
@Slf4j(topic = "c.ChannleProvider")
public class ChannelProvider {
    private static EventLoopGroup eventLoopGroup;
    private static Bootstrap bootstrap = initializeBoostrap();

    private static Bootstrap initializeBoostrap() {
        eventLoopGroup = new NioEventLoopGroup();//本质是一个单线程的执行器（同时维护了一个Selector），里面有run方法处理Channel上源源不断的io事件
        Bootstrap bootstrap = new Bootstrap();//用于客户端的启动类

        //初始化
        bootstrap.group(eventLoopGroup)             //添加EventLoopGroup
                .channel(NioSocketChannel.class)    //选择客户端channel实现
                //连接的超时时间，超过这个时间还是建立不上的话则代表连接失败
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                //是否开启 TCP 底层心跳机制
                .option(ChannelOption.SO_KEEPALIVE, true)   //用来探测对端是否可用。TCP 层将在定时时间到后发送相应的 KeepAlive 探针以确定连接可用性。
                //TCP默认开启了 Nagle 算法，该算法的作用是尽可能的发送大数据快，减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
                .option(ChannelOption.TCP_NODELAY, true);
        return bootstrap;
    }

    private static Map<String, Channel> channels = new ConcurrentHashMap<>();// 不同服务器的InetSocketAddress  建立的channel不同



    public static Channel get(InetSocketAddress inetSocketAddress, CommonSerializer serializer) throws InterruptedException {
        String key = inetSocketAddress.toString() + serializer.getCode();
        if(channels.containsKey(key)) {
            Channel channel = channels.get(key);
            if(channel != null && channel.isActive()) {
                return channel;
            } else {
                channels.remove(key);
            }
        }

        bootstrap.handler(new ChannelInitializer<SocketChannel>() {//启动类 添加处理器
            @Override       //在bootstrap.connect()连接建立后被调用
            protected void initChannel(SocketChannel ch) throws Exception {
                //出站操作
                ch.pipeline().addLast(new CommonEncoder(serializer))
                        .addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS))//IdleStateHandler心跳机制 确保 TCP 连接的有效性
                        .addLast(new CommonDecoder())
                        .addLast(new NettyClientHandler());//自定义客户端 ChannelHandler 来处理服务端发过来的数据
                                // 继承  SimpleChannelInboundHandler   ChannelHandle  处理读事件 返回 RPCResponse类结构体 调给 TCP连接该客户端的channel
            }
        });

        Channel channel = null;
        try {
            channel = connect(bootstrap, inetSocketAddress);
        } catch (ExecutionException e) {
            log.error("连接客户端时有错误发生", e);
            return null;
        }
        channels.put(key, channel);
        return channel;
    }


    private static Channel connect(Bootstrap bootstrap, InetSocketAddress inetSocketAddress) throws ExecutionException, InterruptedException {
        CompletableFuture<Channel> completableFuture = new CompletableFuture<>();
        bootstrap.connect(inetSocketAddress).addListener((ChannelFutureListener) future -> {//使用TCP协议连接服务端 bind()使用UDP  addListener为ChannelFutureListener添加监听器
            if (future.isSuccess()) {
                log.info("客户端连接成功!");
                completableFuture.complete(future.channel());
            } else {
                throw new IllegalStateException();
            }
        });
        return completableFuture.get();//异步获取连接客户端 的 channel 是否成功，成功了get 到 complete方法的返回channel
    }
}
